using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using AzureContrib.WindowsAzure.Properties;
using AzureContrib.WindowsAzure.Utils;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;

namespace AzureContrib.WindowsAzure.StorageClient
{
	/// <summary>
	/// Extensions on ICloudDriveManager
	/// </summary>
	public static class CloudDriveManagerExtensions
	{
		/// <summary>
		/// Create a cloud drive in blob storage that does not exist.
		/// </summary>
		/// <param name="cloudDriveManager">The CloudDrive Manager to extend.</param>
		/// <param name="cloudStorageAccount">The Windows Azure Storage account to access.</param>
		/// <param name="driveRelativePath">The Page Blob Cloud Dive.</param>
		/// <returns>True if the specific Cloud Drive (Page Blob) exists.</returns>
		public static bool Exists(this ICloudDriveManager cloudDriveManager, CloudStorageAccount cloudStorageAccount, string driveRelativePath)
		{
			var driveAddress = cloudStorageAccount.BlobEndpoint.Append(driveRelativePath);
			return cloudDriveManager.Exists(cloudStorageAccount.Credentials, driveAddress);
		}

		/// <summary>
		/// Create a cloud drive in blob storage that does not exist.
		/// </summary>
		/// <param name="cloudDriveManager">The CloudDrive Manager to extend.</param>
		/// <param name="cloudStorageAccount">The Windows Azure Storage account to access.</param>
		/// <param name="driveRelativePath">The Page Blob Cloud Dive.</param>
		/// <param name="sizeInMegabytes">If the drive to mount does not exist then create it with the given size. No value means do not create if it does not exist.</param>
		/// <param name="driveAddress">The address of the Page Blob created.</param>
		/// <returns>True if the specific Cloud Drive (Page Blob) exists.</returns>
		public static bool Exists(this ICloudDriveManager cloudDriveManager, CloudStorageAccount cloudStorageAccount, string driveRelativePath, int? sizeInMegabytes, out Uri driveAddress)
		{
			driveAddress = cloudStorageAccount.BlobEndpoint.Append(driveRelativePath);
			return cloudDriveManager.Exists(cloudStorageAccount, driveRelativePath);
		}

		/// <summary>
		/// Create a cloud drive in blob storage that does not exist.
		/// </summary>
		/// <param name="cloudDriveManager">The CloudDrive Manager to extend.</param>
		/// <param name="cloudStorageAccount">The Windows Azure Storage account to access.</param>
		/// <param name="driveRelativePath">The Page Blob Cloud Dive.</param>
		/// <param name="sizeInMegabytes">If the drive to mount does not exist then create it with the given size. No value means do not create if it does not exist.</param>
		public static Uri Create(this ICloudDriveManager cloudDriveManager, CloudStorageAccount cloudStorageAccount, string driveRelativePath, int sizeInMegabytes)
		{
			var driveAddress = cloudStorageAccount.BlobEndpoint.Append(driveRelativePath);
			cloudDriveManager.Create(cloudStorageAccount.Credentials, driveAddress, sizeInMegabytes);
			return driveAddress;
		}

		/// <summary>
		/// Creates the CloudDrive if it does not already exist.
		/// </summary>
		/// <param name="cloudDriveManager">The CloudDrive Manager to extend.</param>
		/// <param name="cloudStorageAccount">The Windows Azure Storage account to access.</param>
		/// <param name="driveRelativePath">The Page Blob Cloud Dive.</param>
		/// <param name="sizeInMegabytes">If the drive to mount does not exist then create it with the given size. No value means do not create if it does not exist.</param>
		/// <returns>True if the CloudDrive did not already exist and was created; otherwise, false.</returns>
		public static bool CreateIfNotExists(this ICloudDriveManager cloudDriveManager, CloudStorageAccount cloudStorageAccount, string driveRelativePath, int sizeInMegabytes)
		{
			Uri uri;
			return CreateIfNotExists(cloudDriveManager, cloudStorageAccount, driveRelativePath, sizeInMegabytes, out uri);
		}

		/// <summary>
		/// Creates the CloudDrive if it does not already exist.
		/// </summary>
		/// <param name="cloudDriveManager">The CloudDrive Manager to extend.</param>
		/// <param name="cloudStorageAccount">The Windows Azure Storage account to access.</param>
		/// <param name="driveRelativePath">The Page Blob Cloud Dive.</param>
		/// <param name="sizeInMegabytes">If the drive to mount does not exist then create it with the given size. No value means do not create if it does not exist.</param>
		/// <param name="driveAddress">The CloudDrive blob VHD dive address.</param>
		/// <returns>True if the CloudDrive did not already exist and was created; otherwise, false.</returns>
		public static bool CreateIfNotExists(this ICloudDriveManager cloudDriveManager, CloudStorageAccount cloudStorageAccount, string driveRelativePath, int sizeInMegabytes, out Uri driveAddress)
		{
			var exists = cloudDriveManager.Exists(cloudStorageAccount, driveRelativePath);
			if (exists)
			{
				driveAddress = cloudStorageAccount.BlobEndpoint.Append(driveRelativePath);
			}
			else
			{
				driveAddress = cloudDriveManager.Create(cloudStorageAccount, driveRelativePath, sizeInMegabytes);
			}
			return !exists;
		}

		/// <summary>
		/// Creates the CloudDrive if it does not already exist.
		/// </summary>
		/// <param name="cloudDriveManager">The CloudDrive Manager to extend.</param>
		/// <param name="storageCredentials">The Windows Azure Storage Credentials to access Cloud Storage.</param>
		/// <param name="driveAddress">The CloudDrive blob VHD dive address.</param>
		/// <param name="sizeInMegabytes">If the drive to mount does not exist then create it with the given size. No value means do not create if it does not exist.</param>
		/// <returns>True if the CloudDrive did not already exist and was created; otherwise, false.</returns>
		public static bool CreateIfNotExists(this ICloudDriveManager cloudDriveManager, StorageCredentials storageCredentials, Uri driveAddress, int sizeInMegabytes)
		{
			if (cloudDriveManager.Exists(storageCredentials, driveAddress))
			{
				return false;
			}

			cloudDriveManager.Create(storageCredentials, driveAddress, sizeInMegabytes);
			return true;
		}

		/// <summary>
		/// Mount a Windows Azure Drive and return the DirectoryInfo for it.
		/// </summary>
		/// <param name="cloudDriveManager">The CloudDrive Manager to extend.</param>
		/// <param name="cloudStorageAccount">The Windows Azure Storage account to access.</param>
		/// <param name="driveRelativePath">The Page Blob Cloud Dive.</param>
		/// <param name="cacheSize">The part of the cache to take up for this drive.</param>
		/// <param name="driveMountOptions">Drive mounting options.</param>
		/// <returns></returns>
		public static DirectoryInfo Mount(this ICloudDriveManager cloudDriveManager, CloudStorageAccount cloudStorageAccount, string driveRelativePath, int cacheSize = 0, DriveMountOptions driveMountOptions = DriveMountOptions.Force)
		{
			var driveAddress = cloudStorageAccount.BlobEndpoint.Append(driveRelativePath);
			return cloudDriveManager.Mount(cloudStorageAccount.Credentials, driveAddress, cacheSize, driveMountOptions);
		}

		/// <summary>
		/// Unmount all drives mounted on this application.
		/// </summary>
		/// <param name="cloudDriveManager">The CloudDrive Manager to extend.</param>
		public static void UnmountAllDrives(this ICloudDriveManager cloudDriveManager)
		{
			Trace.TraceInformation(Strings.ManagedDrivesUnmounting);

			while (CloudDrive.GetMountedDrives().Any())
			{
				cloudDriveManager.Unmount(new DirectoryInfo(CloudDrive.GetMountedDrives().First().Key));
			}

			Trace.TraceInformation(Strings.ManagedDrivesUnmounted);
		}

		/// <summary>
		/// Unmount and delete from blob storage all drives mounted on this application.
		/// </summary>
		/// <param name="cloudDriveManager">The CloudDrive Manager to extend.</param>
		public static void DeleteAllDrives(this ICloudDriveManager cloudDriveManager)
		{
			Trace.TraceInformation(Strings.ManagedDrivesDeleting);

			while (CloudDrive.GetMountedDrives().Any())
			{
				cloudDriveManager.Delete(new DirectoryInfo(CloudDrive.GetMountedDrives().First().Key));
			}

			Trace.TraceInformation(Strings.ManagedDrivesDeleted);
		}
	}
}